var avatar = null;
var callHistory = null;
var pp = {};
pp.webdb = {};
pp.webdb.db = null;

$(document).ready(function (){
	if (callHistory == null) {
		callHistory = new History();
	}
	pp.webdb.open();
	pp.webdb.createTable();
	pp.webdb.fetchOrderedHistory(callHistory.display);
});

/**
 * Opens a database of max size.
 */
pp.webdb.open = function() {
	var dbSize = 5 * 1024 * 1024; // 5MB
	pp.webdb.db = openDatabase('PreviousCalls', '1.0', 'Previous database calls', dbSize);
}

/**
 * Generic on error callback.
 */
pp.webdb.onError = function(tx, e) {
	alert('Something unexpected happened: ' + e.message );
}

/**
 * Generic on success callback.
 */
pp.webdb.onSuccess = function(tx, r) {
}

pp.webdb.cleardb = function() {
	pp.webdb.db.transaction(function(tx) {
		tx.executeSql('DELETE FROM previous_calls',[])
		tx.executeSql('DELETE FROM parameters',[])
	});
}

/**
 * Creates the previous_calls table.
 */
pp.webdb.createTable = function() {
	pp.webdb.db.transaction(function(tx) {
		tx.executeSql('CREATE TABLE IF NOT EXISTS ' + 
					  'previous_calls(ID INTEGER PRIMARY KEY ASC, api_path TEXT, result TEXT, request_method TEXT, last_action DATETIME)', []);
	});
	
	pp.webdb.db.transaction(function(tx) {
		tx.executeSql('CREATE TABLE IF NOT EXISTS ' + 
					  'parameters(api_path TEXT, parameter TEXT, value TEXT)', []);
	});
}

/**
 * Used to fetch a previous API call and sends the result to a callback
 * @param {int) id - the primary key for the previous api call.
 * @param {function} callback - the function to return the result to
 */
pp.webdb.initFromHistory = function(id, callback) {
	pp.webdb.db.transaction(function(tx) {
		tx.executeSql('SELECT * FROM previous_calls WHERE ID=?',
					  [id],
					  callback,
					  pp.webdb.onError)
	});
}

pp.webdb.fetchParameters = function(api_path, callback) {
	pp.webdb.db.transaction(function(tx) {
		tx.executeSql('SELECT * FROM parameters where api_path=?', [api_path], callback, pp.webdb.onError);		
	});
}

/**
 * Fetches an ordered resultset of all previous calls and dispatches it
 * to a callback
 * 
 * @param {function} callback - receiver of resultset.
 */
pp.webdb.fetchOrderedHistory = function(callback) {
	pp.webdb.db.transaction(function(tx) {
		tx.executeSql('SELECT * FROM previous_calls ORDER BY ID DESC', [], callback, pp.webdb.onError)
	});
}

/**
 * Stores a new call in the database.
 * @param {string} api_path - the path of the API call
 * @param {string} result - the json string representing the API result
 * @param {string} request_method - GET, POST, DELETE or PUT
 * 
 * After this call has completed it goes to pp.webdb.fetchOrderedHistory.
 */
pp.webdb.addPreviousCall = function(api_path, result, request_method, parameters) {
	var last_action = new Date();
	pp.webdb.db.transaction(function(tx) {
		tx.executeSql('INSERT INTO previous_calls(api_path, last_action, result, request_method, last_action) VALUES (?,?,?,?,?)', 
					  [api_path, last_action, result, request_method, last_action],
					  function() {
						pp.webdb.fetchOrderedHistory(callHistory.display);
					  },
					  pp.webdb.onError);
	});
}

pp.webdb.addParametersToCall = function(api_path, parameters) {
	pp.webdb.db.transaction(function(tx) {
		for (item in parameters) {
			tx.executeSql('INSERT INTO parameters(api_path, parameter, value) VALUES (?,?,?)',
					      [api_path, item, parameters[item]]);
		}
	
	});
}

/**
 * Deletes a previous call which matches certain criteria supplied by the parameters.
 * Then inserts a the latest version of the same call.
 * 
 * @param {string} api_path - the path of the API call
 * @param {string} result - the json string representing the API result
 * @param {string} request_method - GET, POST, DELETE or PUT
 * 
 * On success this function routes to pp.webdb.addPreviousCall.
 */
pp.webdb.updatePreviousCall = function(api_path, result, request_method, parameters) {
	pp.webdb.db.transaction(function(tx){
		tx.executeSql('DELETE FROM previous_calls WHERE api_path=? AND request_method=?',
					  [api_path, request_method],
					  function () {
						pp.webdb.addPreviousCall(api_path, result, request_method);
						pp.webdb.updateParametersToCall(api_path, parameters)
					  },
					  pp.webdb.onError);
	});
}

pp.webdb.updateParametersToCall = function(api_path, parameters) {
	pp.webdb.db.transaction(function(tx){
		tx.executeSql('DELETE FROM parameters WHERE api_path=?',
				      [api_path],
				      function () {
						pp.webdb.addParametersToCall(api_path, parameters);
					  },
					  pp.webdb.onError);
	});
}




/**
 * Fetches a specific API call and sends it to the API.requestCallback method
 * 
 * @param {string} api_path - the API path 
 */
pp.webdb.attemptCachedRequest = function(api_path) {
	pp.webdb.db.transaction(function(tx) {
		tx.executeSql('SELECT * FROM previous_calls WHERE api_path=?',
					  [api_path],
					  API.requestCallback,
					  pp.webdb.onError);
		});
}

/**
 * Used for listening for RETURN key press in the api path input field.
 * @param {Event} evt - maybe a keypress event :)
 */
function sendOnReturn(evt) {
	if (evt.keyCode == 13) {
		API.sendRequest();
	}
}

/**
 * Intializes the avatar object if it is not already instantiated.
 * 
 * Then calls avatar.display();
 * @param {String} access_token - the OAuth Access Token
 * @param api_url - the host of the API
 */
function displayAvatar(access_token, api_url) {
	if (avatar == null) {
		avatar = new Avatar(access_token, api_url)
	}
	
	avatar.display();
}


function toggleOptions() {
	$("#options-content").slideToggle();
}
/**
 * Attempts to call the download document API. Opens a new window
 * to return the response in. 
 */
function downloadDocument() {
	var document_id = $("#document_id").val();
	var document_version = $("#document_version_id").val();
	var url = "/doc_download/?api_path=/document/" + document_id + "/";
	var api_url = "/api/v1/document/" + document_id + "/";
	if (document_version != '') {
		url += "&vid=" + document_version;
		api_url += "?version=" + document_version;
	}
	window.open(url)
	
	$("#document_resource_location").html("accessing : " + api_url)
}

/**
 * Constructor for the Avatar object.
 * 
 * @param {String} access_token
 * @param {String} api_url
 * @returns {Avatar}
 */
function Avatar(access_token, api_url) {
	this.dom = {link : "#avatar_link", image : '#avatar_image', user_id : '#avatar_user_id'};
	this.access_token = access_token;
	this.api_url = api_url;
}

/**
 * Displays the avatar.
 */
Avatar.prototype.display = function() {
	if (this.getUserId().length > 0) {
		$(this.dom.link).html(this.getLinkHTML());
		$(this.dom.image).html(this.getImageHTML());
		return;
	}
	
	this.clear();
}

/**
 * Fetches the user id of the user we want to get the avatar for.
 * @returns {String}
 */
Avatar.prototype.getUserId = function() {
	return $(this.dom.user_id).val().trim();
}

/**
 * Constructs html for a link to the avatar.
 * @returns {String}
 */
Avatar.prototype.getLinkHTML = function() {
	return "<input type=\"text\" class=\"avatar_link_url\" value=\"" + this.getUrl() + "\">";
}

/**
 * Constructs html for showing the avatar.
 * @returns {String}
 */
Avatar.prototype.getImageHTML = function() {
	return "<img src=\"" + this.getUrl() + "\"/>";
}

/**
 * Gets the specific url for the avatar.
 * @returns {String}
 */
Avatar.prototype.getUrl = function() {
	return this.api_url + "/avatar/" + this.getUserId() + "/" + this.access_token;
}

/**
 * Clears the avatar display area.
 */
Avatar.prototype.clear = function() {
	$(this.dom.image).html("");
	$(this.dom.link).html("");
}

/**
 * Constructor for the history object.
 * @returns {History}
 */
function History() {
	this.previousCalls = new Array();
}

/**
 * Displays the history based on an SQL result set.
 * 
 * @param {SQLTransaction} tx
 * @param {SQLResultSet} rs 
 */
History.prototype.display = function(tx, rs) {
	var html = "";
	for (var i=0; i < rs.rows.length; i++) {
	    html += callHistory.renderRow(rs.rows.item(i));
	  }
	
	$("#call_history").html(html);
}

/**
 * Returns html for a specific result set row, for display in the history dialogue.
 * @param {SQLResultSetRow} rs
 * @returns {String} 
 */
History.prototype.renderRow = function(row) {
	return "<p class=\"history\"><a href=\"#\" onclick=\"javascript:callHistory.doCall('" + row.ID + "');\">" + row.request_method + " " + row.api_path + "</a></p>";
}

/**
 * Fetches a previous result from the database. Reroutes to the History.callback.
 */
History.prototype.doCall = function(call_id) {
	pp.webdb.initFromHistory(call_id, this.callback);
	setTimeout("API.sendRequest()", 200);
}

/**
 * Receives a result set and handles the first row therein.
 * 
 * Populates the request form, and calls API.sendRequest();
 * 
 * @param {SQLTransaction} tx
 * @param {SQLResultSet} rs 
 */
History.prototype.callback = function(tx, rs) {
	var row = rs.rows.item(0);
	$("#api_path").val(row.api_path);
	pp.webdb.fetchParameters(row.api_path, callHistory.parameterCallback);
}

History.prototype.parameterCallback = function(tx, rs) {
	$('#parameters').empty();
	for (var i = 0; i < rs.rows.length; i++) {
		API.createParameter(rs.rows.item(i).parameter, rs.rows.item(i).value);
	}
	$(CONFIG.dom.jsonOutput).fadeOut()
}

History.prototype.clear = function() {
	pp.webdb.cleardb();
	$("#call_history").html("");
	
}

var CONFIG = {
	dom : {
		jsonOutput : '#jsonOutput',
		tableformat : {
						checked: '#tableformat:checked'
					  },
		apiPath : '#api_path',
		caching : {
					checked: "#option_caching_on:checked"
				  },
		parameter_fields : '#parameters',
		request_method : '#request_verb'
	},
	html_template : {
		parameter_fields : function(parameter, value) {
			var parameter_substitution = "placeholder=\"Name\";";
			var value_substitution = "placeholder=\"Value\"";
			
			if (typeof(parameter) != 'undefined')
			{
				parameter_substitution += " value=\"" + parameter + "\"";
				value_substitution += " value=\"" + value + "\"";
			}	
					
			var randomnumber = Math.floor(Math.random()*1000001);
			var to_return = "";
			to_return += "<div id=\"key_value_pair_" + randomnumber + "\">";
			to_return += "<input type=\"text\" " + parameter_substitution + " parameter_name=\"true\" class=\"parameter_name\">";
			to_return += "=";
			to_return += "<input type=\"text\" " + value_substitution + " parameter_value=\"true\" class=\"parameter_value\">";
			to_return += "<input type=\"button\" value=\"Remove\" onclick=\"API.removeParam('key_value_pair_" + randomnumber + "');\">";
			to_return += "</div>";
			return to_return;
		}
	}
};

var API = {
	URL : "/get/",
	
	/**
	 * Sends a new API request through GET.
	 */
	get : function(api_path, parameters) {
		if (typeof(parameters) == 'undefined') {
			parameters = {'api_path' : api_path};
		}
		else {
			parameters['api_path'] = api_path
		}
		
		$.ajax({
            type: API.currentRequestMethod(),
            url: API.URL,
            data: parameters,
            success: API.parse,
			error: API.errorHandler,
            dataType: 'json'
        });
	},
	
	/**
	 * Adds fields for parameters to the DOM.
	 */
	addParameterFields : function() 
	{
		$(CONFIG.dom.parameter_fields).append(CONFIG.html_template.parameter_fields());
	},
	
	createParameter : function(parameter, value) {
		$(CONFIG.dom.parameter_fields).append(CONFIG.html_template.parameter_fields(parameter, value));
	},
	
	/**
	 * Removes a specific key value parameter pair from the DOM.
	 */
	removeParam : function(id_to_remove) 
	{
		$('#' + id_to_remove).remove();
	},
	
	/**
	 * Checks whether the table format is on or off.
	 */
	tableFormatOn : function()
	{
		return $(CONFIG.dom.tableformat.checked).length > 0;
	},
	
	/**
	 * Checks whether the user wants caching to be on or off.
	 */
	cachingOn : function()
	{
		return $(CONFIG.dom.caching.checked).length > 0;
	},
	
	/**
	 * Gets the current api path.
	 */
	getPath : function ()
	{
		return $(CONFIG.dom.apiPath).val();
	},
	
	/**
	 * Parses a json response.
	 */
	parse : function(api_response) {
		if (API.tableFormatOn()) {
			$(CONFIG.dom.jsonOutput).html(HTML.display(api_response));
		}
		else {
			$(CONFIG.dom.jsonOutput).html(PRETTY.display(api_response));
		}
		API.hideLoading();
		pp.webdb.updatePreviousCall(API.getPath(), JSON.stringify(api_response), API.currentRequestMethod(), API.getParameters());
	},
	
	/**
	 * Used to parse a json response from history (the databse).
	 */
	requestCallback : function(tx, rs) {
		if (rs.rows.length == 0) {
			API.freshRequest();
			return;
		}
		
		var row = rs.rows.item(0);
		API.parseString(row.result);
	},
	
	/**
	 * Returns the current request method as specified in the request form.
	 */
	currentRequestMethod : function() {
		return $(CONFIG.dom.request_method).val();
		
	},
	
	/**
	 * Used to parse and display a stored json response (from the database);
	 */
	parseString : function(stored_api_response) {
		var api_response = JSON.parse(stored_api_response);
		if (API.tableFormatOn()) {
            $(CONFIG.dom.jsonOutput).html(HTML.display(api_response));
        }
        else {
            $(CONFIG.dom.jsonOutput).html(PRETTY.display(api_response));
        }
		
		pp.webdb.updatePreviousCall(API.getPath(), JSON.stringify(api_response), API.currentRequestMethod(), API.getParameters());
	},
	
	getParameters : function() {
		var parameter_name_nodes = $('[parameter_name]');
		var parameter_value_nodes = $('[parameter_value]');
		var parameters = {}
		
		for (var i = 0; i < parameter_name_nodes.length; i++) {
			parameters[parameter_name_nodes.get(i).value] = parameter_value_nodes.get(i).value

		}
		
		return parameters;
	},
	
	/** 
	 * Entry point for the API functionality.
	 */
	sendRequest : function() {
		$(CONFIG.dom.jsonOutput).fadeIn();
		var api_path = API.getPath();
		
		parameters = API.getParameters();
		if (api_path.trim() != "") {
			API.showLoading();
			if (API.cachingOn()) {
				pp.webdb.attemptCachedRequest(api_path)
			}
			else {
				API.get(api_path, parameters);
			}
			return;
		}
		
		$(CONFIG.dom.jsonOutput).html("<p>You need to enter an API URL to be a successfull person :D<br/><br/>Read more about what API:s you can call here: <a href=\"http://code.google.com/p/pp-api-test/wiki/PublicAPIList\">http://code.google.com/p/pp-api-test/wiki/PublicAPIList</a></p>");
	},
	
	/**
	 * Forces a new request to the API regardless of previous calls.
	 */
	freshRequest : function() {
		var api_path = API.getPath();
		API.get(api_path);
	},
	
	/**
	 * Generic error handler for the API.
	 */
	errorHandler : function(data, textstatus, errorthrown) {
		alert(errorthrown + textstatus);
	},
	
	/**
	 * Shows the progress wheel.
	 */
	showLoading : function() {
		$(CONFIG.dom.jsonOutput).html("");
		$(CONFIG.dom.jsonOutput).append('<div class="progress"><img src="/static/progress_wheel.gif" /></div>');
	},
	
	/**
	 * Hides the progress wheel.
	 */
	hideLoading : function() {
		$(".progress").remove();
	}
};
